<!-- 组件中盒子的背景颜色由 box-bg 决定 -->

<script setup lang="ts">
import {onBeforeMount, provide, Ref, ref, watch} from "vue"
import {DataTableRowKey, NDataTable, PaginationProps, DataTableProps} from "naive-ui"
import {arrCopyDeep, isNone} from 'st-common-core'
import {StnDataTableColumns, StnDataTableColumn} from "./types/stn-table.ts"

/**
 * 组件配置选项
 */
defineOptions({
  name: 'StnTable',
})

/**
 * 组件参数
 */
const props = withDefaults(
  defineProps<{
    // 标题
    title?: string;
    // 表格的列配置
    columns: StnDataTableColumns;
    // 是否使用组件中内置的选择列的配置
    isUseBuiltInSelectColumn?: boolean;
    // 表格数据
    data: Array<Object>;
    // 表格数据行唯一标识，如果表格中存在选择列，且表格数据行中不存在 key 字段，则需要传入该组件参数
    rowKey?: (rowData: object) => (number | string);
    // 表格分页配置
    pagination?: PaginationProps;
    // 表格是否显示 loading 状态
    loading?: boolean;
    // 表格内容的横向宽度，如果列被水平固定了，则需要设定它
    scrollX?: number | string;
    // 在进行树型数据选择的时候是否级联
    cascade?: boolean;
    // 表格边框
    dataTableBordered?: boolean;
    // 表格配置
    dataTableProps?: DataTableProps;
  }>(),
  {
    title: '标题',
    isUseBuiltInSelectColumn: true,
    pagination: {} as any,
    loading: false,
    cascade: true,
    dataTableBordered: false,
    dataTableProps: {} as any,
  }
)

/**
 * 组件双向数据绑定
 */
// 表格选中的数据行
const checkedRowKeys = defineModel<DataTableRowKey[]>('checkedRowKeys', {default: []})
// 向子组件提供表格中选中行的 key 组成响应式列表数据
provide<Ref<DataTableRowKey[]>>('checkedRowKeys', checkedRowKeys)
// 展开行的 key 值
const expandedRowKeys = defineModel<DataTableRowKey[]>('expandedRowKeys', {default: []})
// 向子组件提供表格中展开行的 key 组成响应式列表数据
provide<Ref<DataTableRowKey[]>>('expandedRowKeys', expandedRowKeys)

/**
 * 组件自定义事件
 */
const emits = defineEmits<{
  /**
   * 表格异步展开树形数据事件
   */
  (e: 'load-tree-data', rowData: object): Promise<void>
  /**
   * 需要展示的表格列变化时触发的事件
   *
   * @param {"col-showed-change"} e
   * @param {StnDataTableColumns} columns 表格的列配置
   */
  (e: 'col-showed-change', columns: StnDataTableColumns): void;
}>()

// 表格勾选列的配置
const selectableColumn: StnDataTableColumn = {
  type: 'selection',
  className: '',
  title: '勾选列',
  checked: true,
  disableChecked: true,
  draggable: false,
  fixed: 'left',
}
// 表格的列配置
const columns = ref<StnDataTableColumns>(props.columns)
// 需要展示的表格列
const columnsShowed = ref<StnDataTableColumns>([])
/**
 * 预处理表格列配置
 */
onBeforeMount(() => {
  // 使用内置的选择列的配置
  if (props.isUseBuiltInSelectColumn) {
    columns.value = [selectableColumn, ...columns.value]
  }
  // 表格列配置初始值预处理与备份并设置表格列排序
  let idx = 0
  columns.value.forEach((column) => {
    // 如果原来的列配置中具有 checked 属性，且值为 false，则该列的 checked 属性设置为 false，否则设置为 true
    column.checked = !(Reflect.has(column, 'checked') && column.checked === false)
    // 如果原来的列配置中具有 draggable 属性，且值为 false，则该列的 draggable 属性设置为 false，否则设置为 true
    column.draggable = !(Reflect.has(column, 'draggable') && column.draggable === false)
    // 表格列初始状态下，是否选中展示
    column.originChecked = column.checked
    // 如果原来的列配置中不具有 align 属性，则该列的 align 属性设置为 'center'
    column.align = Reflect.has(column, 'align') ? column.align : 'center'
    // 表格列初始状态下，对其方式
    column.originAlign = column.align
    // 表格列初始状态下，是否固定
    column.originFixed = column.fixed
    // 设置表格列的排序
    column.sort = idx++
  })
  // 设置初始状态下展示的表格列
  columnsShowed.value = columns.value
})

// 向子组件提供表格列配置数据
provide<Ref<StnDataTableColumns>>('columns', columns)
provide<Ref<StnDataTableColumns>>('columnsShowed', columnsShowed)

// 监听表格中展示的列的变化
watch(columnsShowed.value, (newVal) => {
  emits('col-showed-change', newVal)
}, {deep: true})

/**
 * 在进行树型数据选择的时候是否级联
 */
const isCascade = ref(props.cascade)
provide<Ref<boolean>>('isCascade', isCascade)

/**
 * 展开所有全部可展开的行
 */
const expandAllHandler = () => {
  // 清空展开行的 Key
  expandedRowKeys.value = []
  // 将所有可展开的行的 Key 加入展开行的 Key
  arrCopyDeep({
    origin: props.data,
    props: ['children'],
    targetPropValMap: {
      children: (origin) => {
        // 如果当前遍历到的数据项存在 children 属性，并且该属性为数组类型，且数组长度大于 0，则该数据项可展开，加入展开行的 Key
        if (Array.isArray(origin.children) && origin.children.length > 0) {
          const key = !isNone(props.rowKey) ? props.rowKey(origin) : (origin.id ?? null)
          if (!isNone(key)) expandedRowKeys.value.push(key)
        }
        return origin.children
      }
    }

  })
}
provide<() => void>('expandAllHandler', expandAllHandler)
const retractAllHandler = () => {
  // 清空展开行的 Key
  expandedRowKeys.value = []
}
provide<() => void>('retractAllHandler', retractAllHandler)
</script>

<template>
  <div class="stn-table">
    <div class="stn-table__search">
      <slot name="search"></slot>
    </div>
    <div class="stn-table__table-container box-bg">
      <div class="stn-table__table-container__top">
        <div class="stn-table__table-container__title">{{ title }}</div>
        <slot name="operations"></slot>
      </div>
      <div class="stn-table__table-container__table">
        <n-data-table
          :style="{ height: '100%' }"
          v-bind="{
            ...dataTableProps,
            columns: columnsShowed,
            data: data,
            rowKey: rowKey,
            bordered: dataTableBordered,
            flexHeight: true,
            maxHeight: '100%',
            pagination: pagination,
            loading: loading,
            scrollX: scrollX,
            cascade: isCascade,
            checkedRowKeys: checkedRowKeys,
            'onUpdate:checkedRowKeys': (keys: DataTableRowKey[]) => checkedRowKeys = keys,
            expandedRowKeys: expandedRowKeys,
            'onUpdate:expandedRowKeys': (keys: DataTableRowKey[]) => expandedRowKeys = keys,
            onload: async (rowData: object) => await emits('load-tree-data', rowData)
          }"
        />
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.stn-table {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;

  .stn-table__table-container {
    flex: 1;
    box-sizing: border-box;
    width: 100%;
    height: 100%;
    padding: 1rem;
    border-radius: 0.5rem;
    display: flex;
    flex-direction: column;

    .stn-table__table-container__top {
      margin-bottom: 1rem;
      display: flex;
      justify-content: space-between;
      align-items: center;

      .stn-table__table-container__title {
        font-size: 1.125rem;
        line-height: 1.75rem;
        font-weight: bold;
        overflow: hidden;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 1;
        line-clamp: 1;
      }
    }

    .stn-table__table-container__table {
      flex: 1;
      box-sizing: border-box;
      width: 100%;
      height: 100%;
      border-radius: 0.25rem;
      overflow: hidden;
    }
  }
}
</style>
